﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GenericGA.extrandom;

namespace GenericGA.generationmaker.crossover
{
    /// <summary>
    /// Cross two chromosomes by swapping individual bits between the chromosomes, each bit having a particular probability of being swapped.
    /// </summary>
    public class UniformCrossover : Crossover
    {

        /// <summary>
        /// A random number generator.
        /// </summary>
        private readonly ExtRandom randGen;

        /// <summary>
        /// The probability for each bit to be swapped.
        /// </summary>
        public double CrossoverRate
        {
            get
            {
                return crossoverRate;
            }

            set
            {
                if (value >= 1.0 || value <= 0.0)
                {
                    throw new ArgumentException();
                }
                crossoverRate = value;
            }
        }
        private double crossoverRate;

        /// <summary>
        /// Constructor to create a UniformCrossover object using a default crossover rate of 0.5.
        /// </summary>
        /// <param name="randGen">A random number generator.</param>
        public UniformCrossover(ExtRandom randGen) : this(randGen, 0.5)
        {
        }

        /// <summary>
        /// Constructor to create a UniformCrossover object.
        /// </summary>
        /// <param name="randGen">A random number generator.</param>
        /// <param name="crossoverRate">The probability of each bit being swapped.</param>
        public UniformCrossover(ExtRandom randGen, double crossoverRate)
        {
            this.randGen = randGen;
            CrossoverRate = crossoverRate;
        }

        /// <summary>
        /// Cross over the genetic material of two chromosomes by overwriting them.
        /// </summary>
        /// <param name="bits1">One of the 2 chromosomes to cross over. Will be modified.</param>
        /// <param name="bits2">One of the 2 chromosomes to cross over. Will be modified.</param>
        public override void Cross(List<ulong> offspring1, List<ulong> offspring2)
        {
            Crossover.MakeLengthsEqual(randGen, offspring1, offspring2);
            for (int i = 0; i < offspring1.Count; i++)
            {
                Crossover.SwapBits(offspring1, i, offspring2, i, randGen.NextULongMask(crossoverRate));
            }
        }

    }
}
